/*
Copyright (C) 2014  Johan Degraeve

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/gpl.txt>.

*/

package helpdiabetesj2me;

import java.util.Timer;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 *
 * @author Johan
 */
public abstract class MyCanvas extends Canvas {
    
    int y = 0;int x = 0; // will be used to keep position where next char, .. can be drawn
    
    // the font to be used in graphics
    Font userfont;
    
    static int scrwidth;//screen width
    static int scrhght;//will be the screen height
    
    // the height of the font being used
    int fonthght;

    //constant values for key_codes, to be used by derived classes to check on keypresses
    static final int Key_code_Change_size=5010;
    static final int Key_code_Close=5011;
    static final int Key_code_UP=5012;
    static final int Key_code_DOWN=5013;
    static final int Key_code_LEFT=5014;
    static final int Key_code_RIGHT=5015;
    static final int Key_code_FIRE=5016;    

    static final int[][] keycodes = {
        {KEY_NUM1,KEY_NUM2,KEY_NUM3,Key_code_Change_size},
        {KEY_NUM4,KEY_NUM5,KEY_NUM6,Key_code_UP},
        {KEY_NUM7,KEY_NUM8,KEY_NUM9,Key_code_DOWN},
        {Key_code_LEFT,KEY_NUM0,Key_code_RIGHT,Key_code_Close}

        };

    // cursor ise a short vertical blinking line, as a high as the font size,
    // this variable will be switched from true to false to true by flipfoptimertask
    // then paint will use the value to determine of cursor needs to be shown or not
    protected boolean showCursor;

    // timer for making the cursor blink
    static private Timer flipflopTimer;
	
    // this object will be used to keep the characters entered by the user
    // and to synchronize communication between methods 'SearchFoodItem' and the methods that handle the key presses (keyConfirmed, keyPressed, ..).
    SearchString enteredstring;
    
    //used in several methods to retrieve record
    protected byte[] b;
    public static final int maxblength = 500;
    static int blength;
    
    //my personal extensions to possible keyCodes for Sony Ericsson. Used in keyPressed to check if back or clear are used.
    static final int BACK_KEY = -11;
    static final int CLEAR_KEY = -8;
    
    //will be the midlet
    protected HelpDiabetes midlet;
    
    //can be used to store the current displayable
    Displayable curdisp;
    
    //used in paintstring
    static int fillUpTo;
    static int temp1;
    static char ch;
    
    // will be used to exit the application
    static Timer exittimer;
    
    // this array defines the sortorder to be used
        // example : the user enters character 's', ascii value of 's' = 115
        // charorder[115] = 224. Excel puts the 's' on place 221 but I'm treating 'S', 's', 'Š', 'š' and 'ß' as the same order.
        // Which means these variants of 's' are considered as equal. 224 is the place where Excel buts 'ß'.
        static final char[] charorder = {  0,   1,  2,  3,  4,  5,  6,  7,  8,  40,
                                           41,  42, 43, 44, 9,  10, 11, 12, 13, 14,
                                           15,  16, 17, 18, 19, 20, 21, 22, 23, 24,
                                           25,  26, 38, 45, 46, 47, 48, 49, 50, 33,
                                           51,  52, 53, 88,  0, 34, 55, 56,115,120,//44 which is ascii code for , is being mapped to 0, this is because , will never be shown in list, should not appear in text, and only appears in byte range as delimiter between the different fields
                                           122,124,125,126,127,128,129,130, 57, 58,
                                           89,  90, 91, 59, 60,147,149,153,157,167,
                                           170,172,174,184,186,188,190,192,196,213,
                                           215,217,219,224,229,239,241,243,245,251,
                                           255, 61, 62, 63, 64, 66, 67,147,149,153,
                                           157,167,170,172,174,184,186,188,190,192,
                                           196,213,215,217,219,224,229,239,241,243,
                                           245,251,255, 68, 69, 70, 71, 27,114, 28,
                                           82 ,170, 85,112,109,110, 65,113,224, 86,
                                           213, 29,255, 30, 31, 80, 81, 83, 84,111,
                                           36,  37, 79,229,224, 87,213, 32,255,251,
                                           39,  72, 97, 98, 99,100, 73,101, 74,102,
                                           147, 93,103, 35,104, 75,105, 92,122,124,
                                           76, 106,107,108, 77,120,213, 94,116,117,
                                           118, 78,147,147,147,147,147,147,147,153,
                                           167,167,167,167,184,184,184,184,157,196,
                                           213,213,213,213,213, 95,213,239,239,239,
                                           239,251,229,224,147,147,147,147,147,147,
                                           147,153,167,167,167,167,184,184,184,184,
                                           157,196,213,213,213,213,213, 96,213,239,
                                           239,239,239,251,229,251};

    
    /**  
    * it's a bit stupid but when creating an instance of this class and after setting display to instance of this class, setscrsize() needs to be called
    * @param userfont the font
    * @param midlet the midlet
    **/
    public MyCanvas(Font userfont,HelpDiabetes midlet)  {
        this.setTitle("HelpDiabetes");
        this.userfont = userfont;
        this.midlet = midlet;
        fonthght = userfont.getHeight();
        showCursor = false;// false means not displayed currently, it will flip flop constantly except when were waiting for a repetition of a keypress
        
        enteredstring = new SearchString();

        if (b == null) {
            b = new byte[maxblength] ;
            blength = 0;
        }
                
        setFullScreenMode(false);
    }
    

    void flipflopthecursor() {
        showCursor = !showCursor;

        //in case of flipflop now need to repaint the whole screen in getitemfromuser.paint()
        repaint();
        resetflipfloptimer(this);
    }

    
    // a version of paintstring without length of the byte array and wrap specified. Length of toprint will be used
    int paintstring(byte[] toprint,boolean invert,Graphics graphics,Font thefont){
        return paintstring(toprint,toprint.length,invert,graphics,thefont,false);
    }

    // a version of paintstring without wrap specified
    int paintstring(byte[] toprint,int length, boolean invert,Graphics graphics,Font thefont){
        return paintstring(toprint,length,invert,graphics,thefont,false);
    }

    // a version of paintstring without length but with wrap specified
    int paintstring(byte[] toprint, boolean invert,Graphics graphics,Font thefont, boolean wrap){
        return paintstring(toprint,toprint.length,invert,graphics,thefont,wrap);
    }
    
    /** to print strings <br>
    * Position is determined by current values of x and y<br>
    * This method will set x and y to new values pointing to the next position where new string can be drawn<br>
    * Will do automatic carriage return when screenwidth is reached<br>
    * Assumption that screen is already set to white before calling this function<br>
    * @param toprint is the string to print in the form of a byte array
    * @param invert true means background black and letters white / false means background white and letters black
    * @param grahpics color at the end of this method undefined.
    * @param thefont the Font to be used
    * @param length is the actual length of the array to be considered
    * @param wrap true means the method will try to put whole words on the next line, this is not the case for the inverted part
    * @return the number of lines needed to draw the line
    */
    int paintstring(byte[] toprint,int length,boolean invert,Graphics graphics,Font thefont, boolean wrap){
        int neededlength;
        int temp2;
        int currenty = y;

        if ((length == 0) | (scrwidth == 0)) //this may happen because paint is called before scrwidth and zoekb get initalised.
            {return 0;}
        graphics.setFont(thefont);
        // if invert = true then first calculate the total length of the string when printed and
        //    set the background to black for the part where the text will arrive
        
        // start by setting color to black, if invert, then color will be set to white during check "if (invert)"
        graphics.setColor(0,0,0);
        
        if (invert) {
            // drawing first the black part, just as long as the text is, taking into account 'carriage return'
            int x1 = x;
            int y1 = y;
            fillUpTo = x1;
            for (temp1 = 0;temp1 < length;temp1++) {
                ch = (char)(0xFF & toprint[temp1]);
                if (ch == '|') {ch = ',';}
                fillUpTo = fillUpTo + thefont.charWidth(ch);
                if ((char)(0xFF & (int)toprint[temp1]) == ',') {
                    break;//if the character is a ; then we stop
                } else if (fillUpTo > scrwidth) {
                    graphics.fillRect(x1,y1,scrwidth - x1,fonthght);
                    fillUpTo = thefont.charWidth(ch);
                    x1=0;y1=y1+fonthght;
                }
                
            }
            graphics.fillRect(x1,y1,fillUpTo - x1,fonthght);
            // if invert then we'll print the strings in white'
            graphics.setColor(255,255,255);
        }
       
        

        fillUpTo = x;//we start drawing at position x
        for(temp1=0; temp1 < length; temp1++) {//using int x1 here
            if (!invert && wrap && ((char)(0xFF & (int)toprint[temp1]) == ' ') && (x > 0)) {
                //we should try to put whole words on the next line
                //and the next character is a space, so a new word starts
                //and we're not at the left side of the screen
                temp2 = 1;
                neededlength = thefont.charWidth(' ');
                while ( (temp1+temp2 < length) && ((char)(0xFF & (int)toprint[temp1+temp2]) != ' ') && ((char)(0xFF & (int)toprint[temp1+temp2]) != ',') ) {
                    neededlength = neededlength + thefont.charWidth((char)(0xFF & (int)toprint[temp1+temp2]));
                    temp2=temp2+1;
                }
                if ( (x + neededlength) > scrwidth) {//so we have a space and the space + the next wordt doesn't fit on the screen anymore
                    //word doesn't fit on the screen - go to next line
                    //increase temp1 because we skip the next space, but we can only do that if that space was not the last character
                    //if the space was the last character, break the for loop, becaue we stop printing
                    if (temp1 < length -1) {
                        temp1=temp1+1;//skip the next space
                        x = 0;
                        fillUpTo=0;
                        y = y + fonthght;
                    } else {
                        break;
                    }
                }
            }
            if ((char)(0xFF & (int)toprint[temp1]) == ',') {
                break;//if the character is a ; then we stop
            } else {
                ch = (char)(0xFF & toprint[temp1]);
                if (ch == '|') {ch = ',';} //this is because the excel macro replace ',' by '|'
                //measure the char to draw
                fillUpTo = fillUpTo + thefont.charWidth(ch);
                //see if a new line is needed
                if (fillUpTo > scrwidth )
                {
                        y = y + fonthght;
                        fillUpTo = thefont.charWidth(ch);
                        x = 0;
                }
                //draw the char
                graphics.drawChar(ch, x, y,Graphics.TOP|Graphics.LEFT);
                x = fillUpTo;
            }
        } 

        //reset color to black, in case invert was true, it is now 255,255,255
        if (invert)
            graphics.setColor(0,0,0);
        return (y - currenty + fonthght)/fonthght;
    }

    // method to set the screen height and width 
    // to avoid having to call getwidt and getheight each time
    void setscrsize() {
        while (midlet.getDisplay().getCurrent() == null ) {
            try {
                Thread.sleep(400);//gives a warning in netbeans but can stay
            } catch (InterruptedException ex) {
            }
        }
         scrhght = midlet.getDisplay().getCurrent().getHeight();
         scrwidth = midlet.getDisplay().getCurrent().getWidth();
    }
    
    // getfirstsemicolon and getnextsemicolon are used together 
    // they return the index in b (attributes in MyCanvas class) of respectively the first and the next semicolon
    // When getfirstsemicolon is called, attribute temp1 will be set to the index, next time getnextsemicolon is called, 
    //        searching will start as of temp1+1
    // if not found or if b = null, returnvalue is -1
    // semicolon to be understood as ','
    int getfirstsemicolon () {
        int i;
        temp1 = -1;
        try {
            for (i = 0;i<blength;i++) {
                if ( ((char)(0xFF & b[i])) == ',') {
                    temp1 = i;
                    break;
                }
            }
        } catch (Exception e) {
            
        } finally {
            
        }
        return temp1;
    }
    
    //this method will only behave correctly if getfirstsemicolon has been called before and if temp1 has not been modified
    int getnextsemicolon () {
        int i;
        i = temp1+1;
        temp1=-1;
        try {
            for (;i<blength;i++) {
                if ( ((char)(0xFF & b[i])) == ',') {
                    temp1 = i;
                    break;
                }
            }
        } catch (Exception e) {
            
        } finally {
            
        }
        return temp1;
    }
    
    /**
    * prints the cursor on the current position x and y 
    */
    void printcursor(Graphics graphics,boolean invert) {
        if (invert) {
            graphics.fillRect(x, y, 2,fonthght);
        }
        if (showCursor) {
            if (invert) {
                graphics.setColor(255, 255, 255);
            }
            graphics.drawLine(x+1, y, x+1, y + fonthght);
            if (invert) {
                graphics.setColor(0, 0, 0);
            }
        }
    }
    
    protected void resetexittimer() {
        if (exittimer != null) {
            exittimer.cancel();
            exittimer = null;
        }
        exittimer = new Timer();
        exittimer.schedule(new ExitTimer(this),600000);
    }

    protected void showNotify() {
        resetexittimer();
        resetflipfloptimer(this);
    }
    
    void newline(int inc) {
        x = 0;
        y = y + fonthght + inc;
    }

    //will reset the flipfloptimer
    //mycanvas : is the displayable for which the timer will be running. The timer itself is a static variable, 
    //so we must make sure that the reset occurs for the current displayable
    public void resetflipfloptimer(Displayable mycanvas) {
        cancelflipfloptimer();
        flipflopTimer = new Timer();
        flipflopTimer.schedule(new FlipFlopCursor(mycanvas),500);
    }

    public void cancelflipfloptimer() {
        if (flipflopTimer != null) {
            flipflopTimer.cancel();
            flipflopTimer = null;
        }
    }

      //method to paint keyboard on screen

    

      //this is a name that will be used here to use hdproperties.
      //name will be used as key to store the type of keyboard in the properties
      abstract String GetClassName() ;
      
      //it's necessary to overload getGameAction because the Canvas.getGameAction throws a runtime exception if keyCode has value in 5000 range
      public int getGameAction(int keyCode) {
      	switch (keyCode) {
      		case Key_code_UP: 
      		   return UP;
      		case Key_code_DOWN: 
      		   return DOWN;
      		case Key_code_LEFT: 
      		   return LEFT;
      		case Key_code_RIGHT: 
      		   return RIGHT;
      		case Key_code_FIRE: 
      		   return FIRE;
      		default:
      			 return super.getGameAction(keyCode);
        }	
      }


    /**
     * @param unitnumber the index to the unit to be used
     * @return carb amount in  b, with b a record from foodrs
     */
    protected Float CalculateCarbAmount_in_b(int unitnumber) {
        int i1,i2;

        Float returnValue;
       
        // read all the values into floats and one int
        i1=getfirstsemicolon();
        //we need to skip a few columns in the food record store (with columns referring to the columns used in excel, depending on the value of unitnumber
        for (int itemp = 1;itemp < unitnumber;itemp++) {
            getnextsemicolon();
            getnextsemicolon();
            getnextsemicolon();
            getnextsemicolon();
            getnextsemicolon();
            getnextsemicolon();
        }

        getnextsemicolon();
        getnextsemicolon();
        getnextsemicolon();
        i2 = getnextsemicolon();

        i1 = i2;
        i2 = getnextsemicolon();
        if (i2 > i1 + 1) {
            returnValue = Float.parse(new String(b,i1+1,i2-i1-1),10);
        } else {
            returnValue = new Float(Float.ERROR);
        }
        return returnValue;
   }
    
    
       /**
        * @param unitnumber the index to the unit to be used
         * @return standard amount in  b, with b a record from foodrs
        */
       public int CalculateStandardAmount_in_b(int unitnumber) {
       int i1,i2;

            // read all the values into floats and one int
            getfirstsemicolon();
            //we need to skip a few columns in the food record store (with columns referring to the columns used in excel, depending on the value of unitnumber
            for (int itemp = 1;itemp < unitnumber;itemp++) {

                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
            }

            i2 = getnextsemicolon();
            //unit = Integer.parseInt(new String(b,i1+1,i2-i1-1));

            i1 = i2;
            i2 = getnextsemicolon();
            if (i2 > i1 + 1) {
                //there's a standard amount filled in, this should be an integer
                return Integer.parseInt(new String(b,i1+1,i2-i1-1));
            } else {
                //standard amount field is empty, we'll use '1'
                return 1;
            }
        }
       
        public String getUnit_in_b(int unitnumber) {
        int i1,i2;

            // read all the values into floats and one int
            i1=getfirstsemicolon();
            //we need to skip a few columns in the food record store (with columns referring to the columns used in excel, depending on the value of unitnumber
            for (int itemp = 1;itemp < unitnumber;itemp++) {

                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
                getnextsemicolon();
                i2=getnextsemicolon();
                i1=i2;
            }

            i2 = getnextsemicolon();
            return new String(b,i1+1,i2-i1-1);
   }

}

